import { MoveDirection } from "./enums"
import GameConfig from "./GameConfig"
import { Square } from "./Square"
import { SquareGroup } from "./SquareGroup"
import { IPoint, IShape } from "./types"

/**
 * 函数重载+类型保护函数
 */
function IsPoint(obj: any): obj is IPoint {
    if (typeof obj.x === "undefined") {
        return false
    }
    return true
}

/**
 * 用于提供规则的处理的函数集合，方便外面调用：目前包含move、canIMove、moveDirectly
 */
export class TerisRule {
    /**
     * 判断是否能够移动
     * @param shape 形状数组
     * @param targetPoint 目标坐标
     * @returns {boolean} 是否能够移动
     */
    static canIMove(shape: IShape, targetPoint: IPoint, exists: Square[]): boolean {
        const targetShapePoints: IPoint[] = shape.map((s) => {
            return {
                x: targetPoint.x + s.x,
                y: targetPoint.y + s.y
            }
        })
        const judge = targetShapePoints.some((p) => {
            if (p.x < 0 || p.x > GameConfig.panelSize.width - 1 || p.y < 0 || p.y > GameConfig.panelSize.height - 1) {
                return true
            }
        })
        // 判断是否和已有方块有重叠
        const existsJudge = targetShapePoints.some((p) => exists.some((sq) => sq.point.x === p.x && sq.point.y === p.y))
        if (existsJudge) {
            return false
        }
        return judge ? false : true
    }
    /**
     * 移动函数重载，2个函数类型
     * @param {SquareGroup} teris
     * @param targetPointOrDirection
     * @returns {boolean} 是否移动成功
     */
    static move(teris: SquareGroup, targetPoint: IPoint, exists: Square[]): boolean
    static move(teris: SquareGroup, direction: MoveDirection, exists: Square[]): boolean
    static move(teris: SquareGroup, targetPointOrDirection: IPoint | MoveDirection, exists: Square[]): boolean {
        if (IsPoint(targetPointOrDirection)) {
            // 如果是坐标，则先判断调用canIMove后移动
            if (this.canIMove(teris.shape, targetPointOrDirection, exists)) {
                teris.centerPoint = {
                    x: targetPointOrDirection.x,
                    y: targetPointOrDirection.y
                }
                return true
            }
            return false
        } else {
            const direction: MoveDirection = targetPointOrDirection
            let targetPoint: IPoint
            if (direction === MoveDirection.down) {
                targetPoint = { x: teris.centerPoint.x, y: teris.centerPoint.y + 1 }
                if (this.canIMove(teris.shape, targetPoint, exists)) {
                    teris.centerPoint = targetPoint
                    return true
                }
            } else if (direction === MoveDirection.left) {
                targetPoint = { x: teris.centerPoint.x - 1, y: teris.centerPoint.y }
                if (this.canIMove(teris.shape, targetPoint, exists)) {
                    teris.centerPoint = targetPoint
                    return true
                }
            } else {
                targetPoint = { x: teris.centerPoint.x + 1, y: teris.centerPoint.y }
                if (this.canIMove(teris.shape, targetPoint, exists)) {
                    teris.centerPoint = targetPoint
                    return true
                }
            }
            return false
        }
    }

    /**
     * 向下到底步
     */
    static moveDirectly(teris: SquareGroup, exists: Square[]) {
        while (this.canIMove(teris.shape, { x: teris.centerPoint.x, y: teris.centerPoint.y + 1 }, exists)) {
            this.move(teris, MoveDirection.down, exists)
        }
        return false
    }

    /**
     * 旋转方法-满足规则的选择
     */
    static rotate(teris: SquareGroup, exists: Square[]): boolean {
        const newShape = teris.afterRotateShape() // 得到新的形状
        if (this.canIMove(newShape, teris.centerPoint, exists)) {
            teris.rotate()
            return true
        }
        return false
    }

    /**
     * 消除函数，根据Game对象的exists进行消除，返回消除的行数
     */
    static deleteSquare(exists: Square[]): number {
        // 1.获取y坐标的数组
        const ys = exists.map((it) => it.point.y)
        // 2.获取最大和最小的数组
        const maxY = Math.max(...ys)
        const minY = Math.min(...ys)
        // 3.循环判断每一行是否可以消除
        let deleteNumber = 0
        for (let y = minY; y <= maxY; y++) {
            // 这行所有坐标是否有square
            if (this.deleteLine(exists, y)) {
                deleteNumber++
            }
        }
        return deleteNumber
    }

    /**
     * 消除一行
     */
    private static deleteLine(exists: Square[], y: number): boolean {
        const squares = exists.filter((sq) => sq.point.y === y)
        if (squares.length === GameConfig.panelSize.width) {
            // 这一行可以消除，什么叫做消除
            squares.forEach((sq) => {
                // 1.从界面中移除掉
                if (sq.viewer) {
                    sq.viewer.remove()
                }
                // 3.逻辑上删除
                const index = exists.indexOf(sq)
                exists.splice(index, 1)
            })
            // 2.剩下的Y坐标比当前的y小的方块，y+1
            exists
                .filter((sq) => sq.point.y < y)
                .forEach((sq) => {
                    sq.point = {
                        x: sq.point.x,
                        y: sq.point.y + 1
                    }
                })
            return true
        }
        return false
    }
}
